#include "server.h"
#include "log.h"
#include "module.h"

#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <sys/signal.h>
#endif

Server::Server()
: stopRequest(false)
{
}

Server::~Server()
{
  for(size_t i = 0; i < modules.size(); ++i)
    delete modules[i];
  for(size_t i = 0; i < sockets.size(); ++i)
    delete sockets[i];
}

void Server::MainThreadDispatcher(void* server)
{
  static_cast<Server*>(server)->MainThread();
}

std::string Server::GetHeader()
{
  if (header.size() != 0)
    return header;
  std::string s;
  s += "<data>";
  s += "<modules>";
  for (size_t i = 0; i < modules.size(); ++i)
  {
    s += "<module><name>";
    s += modules[i]->GetName();
    s += "</name></module>";
  }
  s += "</modules>";
  s += "<user><name>Ivan</name></user>";
  s += "</data>";

  xsltStylesheetPtr cur = xsltParseStylesheetFile((const xmlChar *)"../../data/header.xslt");
  xmlDocPtr doc = xmlParseMemory(s.c_str(), (int)s.size());
  char* params[1];
  params[0] = 0;
  xmlDocPtr res = xsltApplyStylesheet(cur, doc, (const char **)&params);
  xmlChar* htmlString = 0;
  int htmlStringLength = 0;
  xsltSaveResultToString(&htmlString, &htmlStringLength, res, cur);
  header = std::string((char*)htmlString);
  xmlFree(htmlString);
  xmlFreeDoc(doc);
  xsltFreeStylesheet(cur);
  return header;
}

void Server::Start()
{
#ifdef _WIN32
  HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainThreadDispatcher, (void*)this, 0, NULL);
  if (hThread != NULL)
    CloseHandle(hThread);
#else
  signal(SIGPIPE, SIG_IGN);

  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  pthread_t thread_id;
  int result = pthread_create(&thread_id, &attr, (void*(*)(void*))MainThreadDispatcher, (void*)this);
  Log::ErrorIf(result != 0, "Server::Start create thread failed.");
#endif
}

void Server::MainThread()
{
  Socket* socket443 = new Socket();
  sockets.push_back(socket443);
  socket443->EnableSSL();
  socket443->Listen(443);
  Socket* socket80 = new Socket();
  sockets.push_back(socket80);
  socket80->Listen(80);

  while (!stopRequest)
  {
    int i = Socket::CheckConnections(sockets, 500);
    if (i == -1)
    {
      #ifdef _WIN32
      Sleep(1000);
      #endif

      continue;
    }

    sockets[i]->AcceptConnection();
    if (sockets[i]->IsConnected())
    {
      Http request(sockets[i]->ReadString());
      std::string url = request.GetRequestURL();
      if (host.size() == 0)
        host = request.GetRequestHost();
      if (url == "/")
      {
        Http response(Http::Response::Ok);
        response.AddHeader("Connection", "close");
        response.SetContentHTML("<html><frameset rows='200,*' frameborder=0><frame src='header'/><frame width='800' align='center' src='welcome'/></frameset></html>\r\n");
        response.WriteResponseToSocket(*sockets[i]);
        sockets[i]->CloseConnection();
      }
      else if (url == "/welcome")
      {
        Http response(Http::Response::Ok);
        response.AddHeader("Connection", "close");
        response.SetContentHTML("<html><body><table width='800' align='center'><tr><td>welcome</td></tr></table></body></html>\r\n");
        response.WriteResponseToSocket(*sockets[i]);
        sockets[i]->CloseConnection();
      }
      else if (url == "/header")
      {
        Http response(Http::Response::Ok);
        response.AddHeader("Connection", "close");
        response.SetContentHTML(GetHeader());
        response.WriteResponseToSocket(*sockets[i]);
        sockets[i]->CloseConnection();
      }
      else if (url.substr(0, 6) == "/data/")
      {
        Http response(Http::Response::Ok);
        response.AddHeader("Connection", "close");
        response.SetContentBinary("../.." + url);
        response.WriteResponseToSocket(*sockets[i]);
        sockets[i]->CloseConnection();
      }
      else // modules
      {
        Http response(Http::Response::Ok);
        for(size_t j = 0; j < modules.size(); ++j)
          if ("/" + modules[j]->GetName() == url.substr(0, modules[j]->GetName().size() + 1))
            response = modules[j]->ProcessRequest(request);
        response.WriteResponseToSocket(*sockets[i]);
        sockets[i]->CloseConnection();
      }
    }
  }
}

void Server::AddModule(Module* module)
{
  modules.push_back(module);
  module->SetServer(this);
  module->OnAttachedToServer();
}

void Server::AddSocket(int port, Module* module, bool ssl)
{
  Socket* socket = new Socket();
  if (ssl)
    socket->EnableSSL();
  bool success = socket->Listen(port);
  if (success)
    sockets.push_back(socket);
}

std::string Server::GetAddress() const
{
  if (host.size() > 0)
    return host;
  if (ipaddress.size() == 0)
  {
    Socket socket;
    socket.Connect("208.78.70.70", 80); //socket.Connect("75.147.234.41", 80);
    socket.WriteString("GET / HTTP/1.1\r\n\r\n");
    socket.WriteEnd();
    std::string s = socket.ReadString(1000);
    size_t pos = s.find("Current IP Address: ") + 20; //size_t pos = s.rfind('\n') + 1;
    ipaddress = s.substr(pos, s.find('<', pos) - pos);//ipaddress = s.substr(pos);
  }
  return ipaddress;
}